﻿using System;
using FortuneUniverse.Fortune.Algorithms;

namespace FortuneUniverse.Fortune.Dyson
{
  public class PlanetRawData
  {
    public int precision;
    public ushort[] heightData;
    public byte[] modData;
    public Vector3[] vertices;
    public int[] indexMap;
    public int indexMapPrecision;
    public int indexMapDataLength;
    public int indexMapFaceStride;
    public int indexMapCornerStride;
    private static Vector3[] verts200;
    private static Vector3[] verts64;
    private static int[] indexMap200;
    private static int[] indexMap64;
    public static Vector3[] poles = new Vector3[6]
    {
      Vector3.right,
      Vector3.left,
      Vector3.up,
      Vector3.down,
      Vector3.forward,
      Vector3.back
    };
    public VeinData[] veinPool;
    public int veinCursor = 1;
    private int veinCapacity;

    public PlanetRawData(int _precision)
    {
      this.precision = _precision;
      int dataLength = this.dataLength;
      this.heightData = new ushort[dataLength];
      this.vertices = new Vector3[dataLength];
      this.indexMapPrecision = this.precision >> 2;
      this.indexMapFaceStride = this.indexMapPrecision * this.indexMapPrecision;
      this.indexMapCornerStride = this.indexMapFaceStride * 3;
      this.indexMapDataLength = this.indexMapCornerStride * 8;
      this.indexMap = new int[this.indexMapDataLength];
      this.SetVeinCapacity(32);
    }

    public PlanetRawData()
    {
    }

    public byte[] InitModData(byte[] refModData)
    {
      this.modData = refModData == null ? new byte[this.dataLength / 2] : refModData;
      return this.modData;
    }

    public void Free()
    {
      this.precision = 0;
      this.heightData = (ushort[]) null;
      this.vertices = (Vector3[]) null;
      this.veinPool = (VeinData[]) null;
    }

    public int dataLength => (this.precision + 1) * (this.precision + 1) * 4;

    public int stride => (this.precision + 1) * 2;

    public int substride => this.precision + 1;

    public void SeedExt_CalcVerts()
    {
      if (this.precision == 200 && PlanetRawData.verts200 != null)
      {
        this.vertices = PlanetRawData.verts200;
        this.indexMap = PlanetRawData.indexMap200;
      }
      else if (this.precision == 64 && PlanetRawData.verts64 != null)
      {
        this.vertices = PlanetRawData.verts64;
        this.indexMap = PlanetRawData.indexMap64;
      }
      else
      {
        for (int index = 0; index < this.indexMapDataLength; ++index)
          this.indexMap[index] = -1;
        int num1 = (this.precision + 1) * 2;
        int num2 = this.precision + 1;
        for (int index1 = 0; index1 < this.dataLength; ++index1)
        {
          int num3 = index1 % num1;
          int num4 = index1 / num1;
          int num5 = num3 % num2;
          int num6 = num4 % num2;
          int num7 = ((num3 < num2 ? 0 : 1) + (num4 < num2 ? 0 : 1) * 2) * 2 + (num5 < num6 ? 1 : 0);
          float num8 = num5 < num6 ? (float) num5 : (float) (this.precision - num5);
          float num9 = num5 < num6 ? (float) (this.precision - num6) : (float) num6;
          float num10 = (float) this.precision - num9;
          float t1 = num9 / (float) this.precision;
          float t2 = (double) num10 <= 0.0 ? 0.0f : num8 / num10;
          Vector3 pole1;
          Vector3 pole2;
          Vector3 pole3;
          int corner;
          switch (num7)
          {
            case 0:
              pole1 = PlanetRawData.poles[2];
              pole2 = PlanetRawData.poles[0];
              pole3 = PlanetRawData.poles[4];
              corner = 7;
              break;
            case 1:
              pole1 = PlanetRawData.poles[3];
              pole2 = PlanetRawData.poles[4];
              pole3 = PlanetRawData.poles[0];
              corner = 5;
              break;
            case 2:
              pole1 = PlanetRawData.poles[2];
              pole2 = PlanetRawData.poles[4];
              pole3 = PlanetRawData.poles[1];
              corner = 6;
              break;
            case 3:
              pole1 = PlanetRawData.poles[3];
              pole2 = PlanetRawData.poles[1];
              pole3 = PlanetRawData.poles[4];
              corner = 4;
              break;
            case 4:
              pole1 = PlanetRawData.poles[2];
              pole2 = PlanetRawData.poles[1];
              pole3 = PlanetRawData.poles[5];
              corner = 2;
              break;
            case 5:
              pole1 = PlanetRawData.poles[3];
              pole2 = PlanetRawData.poles[5];
              pole3 = PlanetRawData.poles[1];
              corner = 0;
              break;
            case 6:
              pole1 = PlanetRawData.poles[2];
              pole2 = PlanetRawData.poles[5];
              pole3 = PlanetRawData.poles[0];
              corner = 3;
              break;
            case 7:
              pole1 = PlanetRawData.poles[3];
              pole2 = PlanetRawData.poles[0];
              pole3 = PlanetRawData.poles[5];
              corner = 1;
              break;
            default:
              pole1 = PlanetRawData.poles[2];
              pole2 = PlanetRawData.poles[0];
              pole3 = PlanetRawData.poles[4];
              corner = 7;
              break;
          }
          this.vertices[index1] = Vector3.Slerp(Vector3.Slerp(pole1, pole3, t1), Vector3.Slerp(pole2, pole3, t1), t2);
          int index2 = this.PositionHash(this.vertices[index1], corner);
          if (this.indexMap[index2] == -1)
            this.indexMap[index2] = index1;
        }
        int num11 = 0;
        for (int index = 1; index < this.indexMapDataLength; ++index)
        {
          if (this.indexMap[index] == -1)
          {
            this.indexMap[index] = this.indexMap[index - 1];
            ++num11;
          }
        }
        if (this.precision == 200)
        {
          if (PlanetRawData.verts200 != null)
            return;
          PlanetRawData.verts200 = this.vertices;
          PlanetRawData.indexMap200 = this.indexMap;
        }
        else
        {
          if (this.precision != 64 || PlanetRawData.verts64 != null)
            return;
          PlanetRawData.verts64 = this.vertices;
          PlanetRawData.indexMap64 = this.indexMap;
        }
      }
    }

    public int GetModLevel(int index) => (int) this.modData[index >> 1] >> ((index & 1) << 2) & 3;

    public short GetModPlane(int index) => (short) (((int) this.modData[index >> 1] >> ((index & 1) << 2) + 2 & 3) * 133 + 20020);

    public void EraseVegetableAtPoint(Vector3 vpos)
    {
    }

    public float QueryHeight(Vector3 vpos)
    {
      vpos.Normalize();
      int index1 = this.indexMap[this.PositionHash(vpos)];
      float num1 = (float) (3.14159274101257 / (double) (this.precision * 2) * 1.20000004768372);
      float num2 = num1 * num1;
      float num3 = 0.0f;
      float num4 = 0.0f;
      int stride = this.stride;
      for (int index2 = -1; index2 <= 3; ++index2)
      {
        for (int index3 = -1; index3 <= 3; ++index3)
        {
          int index4 = index1 + index2 + index3 * stride;
          if ((ulong) index4 < (ulong) this.dataLength)
          {
            float sqrMagnitude = (this.vertices[index4] - vpos).sqrMagnitude;
            if ((double) sqrMagnitude <= (double) num2)
            {
              float num5 = (float) (1.0 - (double) Mathf.Sqrt(sqrMagnitude) / (double) num1);
              float num6 = (float) this.heightData[index4];
              num3 += num5;
              num4 += num6 * num5;
            }
          }
        }
      }
      return (double) num3 == 0.0 ? (float) this.heightData[0] * 0.01f : (float) ((double) num4 / (double) num3 * 0.00999999977648258);
    }

    private void SetVeinCapacity(int newCapacity)
    {
      VeinData[] veinPool = this.veinPool;
      this.veinPool = new VeinData[newCapacity];
      if (veinPool != null)
        Array.Copy((Array) veinPool, (Array) this.veinPool, newCapacity <= this.veinCapacity ? newCapacity : this.veinCapacity);
      this.veinCapacity = newCapacity;
    }

    public int AddVeinData(VeinData vein)
    {
      vein.id = this.veinCursor++;
      if (vein.id == this.veinCapacity)
        this.SetVeinCapacity(this.veinCapacity * 2);
      this.veinPool[vein.id] = vein;
      return vein.id;
    }

    private int trans(float x, int pr)
    {
      int num = (int) (((double) Mathf.Sqrt(x + 0.23f) - 0.479583203792572) / 0.629470527172089 * (double) pr);
      if (num >= pr)
        num = pr - 1;
      return num;
    }

    public int PositionHash(Vector3 v, int corner = 0)
    {
      if (corner == 0)
        corner = ((double) v.x <= 0.0 ? 0 : 1) + ((double) v.y <= 0.0 ? 0 : 2) + ((double) v.z <= 0.0 ? 0 : 4);
      if ((double) v.x < 0.0)
        v.x = -v.x;
      if ((double) v.y < 0.0)
        v.y = -v.y;
      if ((double) v.z < 0.0)
        v.z = -v.z;
      if ((double) v.x < 1E-06 && (double) v.y < 1E-06 && (double) v.z < 1E-06)
        return 0;
      int num1;
      int num2;
      int num3;
      if ((double) v.x >= (double) v.y && (double) v.x >= (double) v.z)
      {
        num1 = 0;
        num2 = this.trans(v.z / v.x, this.indexMapPrecision);
        num3 = this.trans(v.y / v.x, this.indexMapPrecision);
      }
      else if ((double) v.y >= (double) v.x && (double) v.y >= (double) v.z)
      {
        num1 = 1;
        num2 = this.trans(v.x / v.y, this.indexMapPrecision);
        num3 = this.trans(v.z / v.y, this.indexMapPrecision);
      }
      else
      {
        num1 = 2;
        num2 = this.trans(v.x / v.z, this.indexMapPrecision);
        num3 = this.trans(v.y / v.z, this.indexMapPrecision);
      }
      return num2 + num3 * this.indexMapPrecision + num1 * this.indexMapFaceStride + corner * this.indexMapCornerStride;
    }
  }
}
